﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;



namespace simDC
{
    public class Constants
    {
        //public static Constants Const = new Constants();

        public static readonly int DATA_MEMORY = 128;
        public static readonly int CODE_MEMORY = 128;
        public static readonly int STACK_MEMORY = 128;
        public static readonly int INTV_MEMORY = 16;
        public static readonly int REGISTER_NUMBER = 16;

        private Constants() { }

    }



    public class Processor
    {
        //public static Processor global_processor = new Processor();
        public static Processor P = new Processor();

        public Int16[] dataMemory;
        public UInt16[] codeMemory;
        public Int16[] stackMemory;
        public Int16[] intMemory;
        public Int16 FLAG = 0;
        public Int16 T = 0;
        public Int16 M = 0x0000;
        public Int16 PC = 0x0000;
        public Int16 SP = 0;
        public Int16 IVR = 0;
        public Int16 ADR = 0;
        public Int16 MDR = 0;
        public UInt16 IR = 0;
        public bool bpo = true;

        public bool g = false; // conditie de salt
        public bool CIL = false; // sau le fac in reg de flaguri dc trebuie :D:) 
        public bool ACLOW = false;
        public bool INTR = false;
        public bool HALT = false;

        public Int16[] R = new Int16[Constants.REGISTER_NUMBER];
        public Mem MEM = new Mem();
        public SBUS SBUS = new SBUS();
        public DBUS DBUS = new DBUS();
        public Alu ALU = new Alu();
        public Rbus RBUS = new Rbus();
        public OtherOperation OTHER_OP = new OtherOperation();




        private Processor()
        {
            dataMemory = new Int16[Constants.DATA_MEMORY];
            codeMemory = new UInt16[Constants.CODE_MEMORY];
            stackMemory = new Int16[Constants.STACK_MEMORY];
            intMemory = new Int16[Constants.INTV_MEMORY];
            R[0] = 0;

        }

        public void ResetSignals()
        {
            MEM.Reset();
            SBUS.Reset();
            DBUS.Reset();
            ALU.Reset();
            RBUS.Reset();
            OTHER_OP.Reset();
        }

        public void ResetAll()
        {
            dataMemory = new Int16[Constants.DATA_MEMORY];
            codeMemory = new UInt16[Constants.CODE_MEMORY];
            stackMemory = new Int16[Constants.STACK_MEMORY];
            intMemory = new Int16[Constants.INTV_MEMORY];
            R = new Int16[Constants.REGISTER_NUMBER];
            MEM.Reset();
            SBUS.Reset();
            DBUS.Reset();
            ALU.Reset();
            RBUS.Reset();
            OTHER_OP.Reset();
            FLAG = 0;
            T = 0;
            M = 0x0000;
            PC = 0x0000;
            SP = (short)(stackMemory.Length*2);
            IVR = 0;
            ADR = 0;
            MDR = 0;
            IR = 0;
            g = false;
            SBUS.PdSBUS = false;
            DBUS.PdDBUS = false;
        }

        public void ComputeSBUS(String str)
        {
            ResetSignals();
            str = str.ToLower();
            SBUS.Reset();
            SBUS.PdSBUS = true;

            switch (str)
            {
                case "none": SBUS.PdSBUS = false; break;
                case "pdflags": SBUS.PdFlag = true; SBUS.Value = FLAG; break;
                case "pdregs": SBUS.PdReg = true; SBUS.Value = R[(IR & 0x03c0) >> 6]; break;
                case "pdsps": SBUS.PdSp = true; SBUS.Value = SP; break;
                case "pdpcs": SBUS.PdPc = true; SBUS.Value = PC; break;
                case "pdts": SBUS.PdT = true; SBUS.Value = T; break;
                case "pdivrs": SBUS.PdIVR = true; SBUS.Value = IVR; break;
                case "pdadrs": SBUS.PdADR = true; SBUS.Value = ADR; break;
                case "pdmdrs": SBUS.PdMDR = true; SBUS.Value = MDR; break;
                case "pdirs": SBUS.PdIR = true; SBUS.Value = (Int16)IR; break;
                case "pdir[offset]": SBUS.PdIR = true; SBUS.Value = (short)(IR & 0x00FF); break;
                case "pd_0": SBUS.Value = 0; break;
                case "pd_1s": SBUS.Value = 1; break;
                case "pd_-1s": SBUS.Value = -1; break;
                case "pdn_ts": SBUS.Value = (short)(( ~T)+1); break;

            }
        }

        public void ComputeDBUS(String str)
        {

            DBUS.Reset();
            str = str.ToLower();
            DBUS.PdDBUS = true;
            switch (str)
            {
                case "none": DBUS.PdDBUS = false; break;
                case "pdflagd": DBUS.PdFlag = true; DBUS.Value = FLAG; break;
                case "pdregd": DBUS.PdReg = true; DBUS.Value = R[(IR & 0x000F)]; break;
                case "pdspd": DBUS.PdSp = true; DBUS.Value = SP; break;
                case "pdpcd": DBUS.PdPc = true; DBUS.Value = PC;/* if (SBUS.PdIR) DBUS.Value += -2;*/ break;
                case "pdtd": DBUS.PdT = true; DBUS.Value = T; break;
                case "pdivrd": DBUS.PdIVR = true; DBUS.Value = IVR; break;
                case "pdadrd": DBUS.PdADR = true; DBUS.Value = ADR; break;
                case "pdmdrd": DBUS.PdMDR = true; DBUS.Value = MDR; break;
                case "pdird": DBUS.PdIR = true; DBUS.Value = (Int16)IR; break;
            }
        }

        public void ComputeRBUS(String str)
        {
            short v = RBUS.Value;
            RBUS.Reset();
            str = str.ToLower();
            switch (str)
            {
                case "pmflag": RBUS.PmFlag = true; FLAG = v; break;               
                case "pmsp": RBUS.PmSp = true; SP = v; break;
                case "pmpc": RBUS.PmPC = true; PC = v; break;
                case "pmt": RBUS.PmT = true; T = v; break;
                case "pmivr": RBUS.PmIVR = true; IVR = v; break;
                case "pmadr": RBUS.PmADR = true; ADR = v; break;
                case "pmmdr": RBUS.PmMDR = true; MDR = v; break;
                case "pmreg": RBUS.PmReg = true; R[IR & 0x000f] = RBUS.Value; 
                                            
                break;
            }
            R[0] = 0;
        }

        public void ComputeALU(String str)
        {
            str = str.ToLower();
            ALU.Reset();
            ALU.PdALU = true;
            ALU.Flag = true;
            switch (str)
            {
                case "none":  ALU.PdALU = false; ALU.Flag = false; break;
                case "~sbus": ALU.NSBUS = true; RBUS.Value = (Int16)(SBUS.Value * (-1)); ALU.Flag = false; break;
                case "sbus": ALU.SBUS = true; RBUS.Value = SBUS.Value; ALU.Flag = false; break;
                case "dbus": ALU.DBUS = true; RBUS.Value = DBUS.Value; ALU.Flag = false; break;

                case "and": ALU.And = true;  RBUS.Value = (Int16)(SBUS.Value & DBUS.Value); setFlags(); break;
                case "sum": ALU.Add = true; RBUS.Value = (Int16)(SBUS.Value + DBUS.Value); setFlags(); break;
                case "or": ALU.Or = true;  RBUS.Value = (Int16)(SBUS.Value | DBUS.Value); setFlags(); break;
                case "xor": ALU.Xor = true; RBUS.Value = (Int16)(SBUS.Value ^ DBUS.Value); setFlags(); break;
                case "mul": ALU.Mul = true; RBUS.Value = (Int16)(SBUS.Value * DBUS.Value); setFlags(); break;
                default: ALU.Flag = false; break;
                                        
            }
        }
        public void setFlags()
        {
            if (RBUS.Value == 0) FLAG = (short)(FLAG | 0x0002); // Z=1
            else
            {
                FLAG = (short)(FLAG & 0x0fffd); //Z=0
                if (RBUS.Value > 0) FLAG = (short)(FLAG & 0xfffb);// S = 0
                else
                {
                    FLAG = (short)(FLAG | 0x0004); // S = 1
                }
            }
        }
        public void ComputeMEM(String str)
        {
            str = str.ToLower();
            MEM.Reset();
            try
            {
                switch (str)
                {
                    case "none": MEM.NONE = true; break;
                    case "ifch":
                        if (OTHER_OP.Plus_2Pc)
                        {
                            MEM.IFCH = true;
                            IR = (UInt16)codeMemory[ADR / 2];
                        }
                        else
                        {
                            MEM.READ = true;
                            MDR = (short)codeMemory[ADR / 2];
                        }
                        break;
                    case "read":
                        MDR = dataMemory[ADR / 2];
                        if (SBUS.PdSp)
                            MDR = stackMemory[ADR / 2];
                        if (SBUS.PdIVR)
                            MDR = intMemory[ADR / 2]; break;
                    case "write": MEM.WRITE = true;

                        if (SBUS.PdSp)
                            stackMemory[ADR / 2] = MDR;
                        else
                            dataMemory[ADR / 2] = MDR;
                        Simulator.updateMemory.Set();
                        break;
                }
            }
            catch 
            {
                MessageBox.Show("Memory violation at address: " + ADR/2 +"!", "Error",
                  MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void ComputeOTHER(String str)
        {
            OTHER_OP.Reset();
            str = str.ToLower();
            OTHER_OP.Flags = true;
            switch (str)
            {
                case "nop": OTHER_OP.Flags = false; break;
                case "cin": FLAG = (Int16)(FLAG | 0x0001); break;
                case "-2sp": SP -= 2; OTHER_OP.Flags = false; OTHER_OP.Minus_2SP = true; break;
                case "+2sp": SP += 2; OTHER_OP.Flags = false; OTHER_OP.Plus_2Sp = true; break;
                case "+2pc": PC += 2; OTHER_OP.Flags = false; OTHER_OP.Plus_2Pc = true; break;
                case "-2pc": PC -= 2; OTHER_OP.Flags = false; OTHER_OP.Plus_2Pc = true; break;
                case "asl": M = (Int16)(M << 1); OTHER_OP.Flags = false; break;
                case "asr": M = (Int16)(M / 2); OTHER_OP.Flags = false; break;
                case "lsr": M = (Int16)(M >> 1); OTHER_OP.Flags = false; break;
                case "rol": OTHER_OP.Flags = false; break;
                case "ror": OTHER_OP.Flags = false; break;
                case "rlc": break;
                case "rrc": break;
                case "a(0)c": FLAG = (Int16)(FLAG & 0xFFFE); break;
                case "a(0)z": FLAG = (Int16)(FLAG & 0xFFFD); break;
                case "a(0)s": FLAG = (Int16)(FLAG & 0xFFFB); break;
                case "a(0)v": FLAG = (Int16)(FLAG & 0xFFF7); break;
                case "a(0)bvi": FLAG = (Int16)(FLAG & 0xFFEF); break;
                case "a(1)c": FLAG = (Int16)((UInt16)FLAG | 0x0001); break;
                case "a(1)z": FLAG = (Int16)((UInt16)FLAG | 0x0002); break;
                case "a(1)s": FLAG = (Int16)((UInt16)FLAG | 0x0004); break;
                case "a(1)v": FLAG = (Int16)((UInt16)FLAG | 0x0008); break;
                case "a(1)bvi": FLAG = (Int16)((UInt16)FLAG | 0x0010); break;
                case "a(0)cszv": FLAG = (Int16)(FLAG & 0xFFF0); break;
                case "a(1)cszv": FLAG = (Int16)((UInt16)FLAG | 0x000F); break;
                case "a(0)bpo": bpo = false; break;
                case "pd_cond": setFlags(); break;
                default: OTHER_OP.Flags = false; break;
            }
        }

        public void Carry(bool v)
        {
            if (v)
                FLAG = (short)(FLAG | 0x0001);
            else
                FLAG = (short)(FLAG & 0xFFFE);




        }
    }
        public class Rbus
        {
            public Int16 Value = 0;
            public bool PmFlag = false;
            public bool PmReg = false;
            public bool PmSp = false;
            public bool PmT = false;
            public bool PmPC = false;
            public bool PmIVR = false;
            public bool PmADR = false;
            public bool PmMDR = false;

            public Rbus() { }
            public void Reset()
            {
               // Value = 0;
                PmFlag = false;
                PmReg = false;
                PmSp = false;
                PmT = false;
                PmPC = false;
                PmIVR = false;
                PmADR = false;
                PmMDR = false;
            }
        }


        public class BUS
        {
            public bool PdFlag = false;
            public bool PdReg = false;
            public bool PdSp = false;
            public bool PdPc = false;
            public bool PdT = false;
            public bool PdIVR = false;
            public bool PdADR = false;
            public bool PdMDR = false;
            public bool PdIR = false;
            public Int16 Value = 0;

            public BUS() { }

            public void Reset()
            {
                Value = 0;
                PdFlag = false;
                PdReg = false;
                PdSp = false;
                PdPc = false;
                PdT = false;
                PdIVR = false;
                PdADR = false;
                PdMDR = false;
                PdIR = false;

            }

        }

        public class SBUS : BUS
        {
            public bool PdSBUS = false;

        }

        public class DBUS : BUS
        {
            public bool PdDBUS = false;
        }

        public class Alu
        {
            public bool NSBUS = false;
            public bool DBUS = false;
            public bool Add = false;
            public bool And = false;
            public bool Or = false;
            public bool Xor = false;
            public bool SBUS = false;
            public bool PdALU = false;
            public bool Flag = false;
            public bool Mul = false;

            public Alu() { }

            public void Reset()
            {
                NSBUS = false;
                DBUS = false;
                Add = false;
                And = false;
                Or = false;
                Xor = false;
                Flag = false;
                PdALU = false;
                SBUS = false;
                Mul = false;
            }

        }

        public class Mem
        {
            public bool IFCH = false;
            public bool READ = false;
            public bool NONE = false;
            public bool WRITE = false;


            public void Reset()
            {
                IFCH = false;
                READ = false;
                NONE = false;
                WRITE = false;
            }

        }

        public class OtherOperation
        {
            public bool NOP = false;
            public bool Cin = false;
            public bool Minus_2SP = false;
            public bool Plus_2Sp = false;
            public bool Plus_2Pc = false;
            public bool ASL = false;
            public bool ASR = false;
            public bool LSR = false;
            public bool ROL = false;
            public bool ROR = false;
            public bool RLC = false;
            public bool RRC = false;
            public bool Reset_C = false;
            public bool Set_C = false;
            public bool Reset_Z = false;
            public bool Set_Z = false;
            public bool Reset_S = false;
            public bool Set_S = false;
            public bool Reset_V = false;
            public bool Set_V = false;
            public bool Reset_CZSV = false;
            public bool Set_CZSV = false;
            public bool Reset_BVI = false;
            public bool Set_BVI = false;
            public bool Flags = false;

            public OtherOperation() { }

            public void Reset()
            {
                NOP = false;
                Cin = false;
                Minus_2SP = false;
                Plus_2Sp = false;
                Plus_2Pc = false;
                ASL = false;
                ASR = false;
                LSR = false;
                ROL = false;
                ROR = false;
                RLC = false;
                RRC = false;
                Reset_C = false;
                Set_C = false;
                Reset_Z = false;
                Set_Z = false;
                Reset_S = false;
                Set_S = false;
                Reset_V = false;
                Set_V = false;
                Reset_CZSV = false;
                Set_CZSV = false;
                Reset_BVI = false;
                Set_BVI = false;
                Flags = false;
            }
        }

    }

